package top.codedance.iotp.manage;

import com.alibaba.fastjson.JSONArray;
import top.codedance.iotp.common.constent.Command;
import top.codedance.iotp.common.entity.CommonEntity;
import top.codedance.iotp.common.util.AsymmetricEncryption;
import top.codedance.iotp.common.util.StringUtils;
import top.codedance.iotp.common.util.SystemUtil;
import top.codedance.iotp.manage.client.Client;
import top.codedance.iotp.manage.client.annotation.ConsoleCommandParameter;
import top.codedance.iotp.server.ServerApplication;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static top.codedance.iotp.manage.client.configration.Command.*;

public class Console {
    public static volatile AsymmetricEncryption.KeyStringPair keyStringPair; //通讯密钥对
    public static volatile String serverPublicKey; //服务端公钥
    public static final String LOGGER_LEVEL = "ERROR";
    private static final String COMMAND_PREFIX = StringUtils.colourString(34, "$>");
    public static ExecutorService pool = Executors.newSingleThreadExecutor(); //线程池管理
    public static ExecutorService workerPool = Executors.newCachedThreadPool(); //同步任务线程池
    public static final String IOT_HISTORY_NAME = ".iot_history"; //保存命令历史的文件
    public static List<String> history = new ArrayList<>();
    public static Client client;
    private static int port = 10011;
    private static boolean isOutEnter = false;

    public static void main(String[] args) {
        String portStr = SystemUtil.readTempFile(ServerApplication.TMP_FILE_NAME);
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            if (portStr != null) {
                port = Integer.parseInt(portStr);
            }
        }
        pool.submit(() -> {
            while (true) {
                try {
                    client = new Client("127.0.0.1", port);
                    client.run();
                } catch (Exception ex) {
                    syslogInfo("Connect fail!");
                }
                if (!Client.isUserClosed()) {
                    try {
                        Thread.sleep(2000);
                        syslogInfo("Reconnecting ... ");
                    } catch (Exception ex) {
                    }
                } else {
                    System.out.println("\n");
                    break;
                }
            }
        });

        String historyData = SystemUtil.readTempFile(IOT_HISTORY_NAME);
        if(historyData != null && !historyData.isEmpty()){
            JSONArray parse = (JSONArray)JSONArray.parse(historyData);
            history = parse.toJavaList(String.class);
        }

        Scanner input = new Scanner(System.in);
        Field[] fields = top.codedance.iotp.manage.client.configration.Command.class.getFields();
        ConsoleCommandParameter annotation;
        String allCommand;
        String[] cmdSplits;
        String command;
        String tmp;
        int historyNo;
        Pattern pattern = Pattern.compile("!(\\d+)");
        Matcher matcher;
        int autoInputNo = -1;
        do {
            boolean isLegitimateCmd = false;
            if(autoInputNo != -1){
                allCommand = history.get(autoInputNo);
            }else {
                allCommand = input.nextLine();
            }
            autoInputNo = -1;
            isOutEnter = false;
            cmdSplits = allCommand.split("\\s+");
            command = cmdSplits[0].toLowerCase();

            matcher = pattern.matcher(command);
            if(matcher.find()){
                historyNo = Integer.valueOf(matcher.group(1));
                tmp = history.get(historyNo);
                if(tmp != null){
                    autoInputNo = historyNo;
                }else{
                    syslogInfo("history no not found.");
                }
                continue;
            }
            if (command.isEmpty() || command.equals("\n")) {
                syslogInfon("");
                continue;
            }

            for (Field f : fields) {
                try {
                    if(f.get(null).toString().toLowerCase().equals(command)) {
                        isLegitimateCmd = true;
                        break;
                    }
                } catch (Exception e) {}
            }
            if(!isLegitimateCmd){
                syslogInfo("Not find command \"" + command + "\"!");
                continue;
            }else{
                if(!command.equals(HISTORY))
                    history.add(allCommand);
            }

            if(command.equals(CLS)){
                JSONArray ja = new JSONArray();
                SystemUtil.genTempFile(IOT_HISTORY_NAME, ja.toJSONString());
                history.clear();
                syslogInfon("");
                continue;
            }

            if (command.equals(QUIT)) {
                JSONArray ja = new JSONArray();
                ja.addAll(history);
                SystemUtil.genTempFile(IOT_HISTORY_NAME, ja.toJSONString());
                client.shutdown();
                System.exit(0);
                break;
            }
            if (command.equals(HELP)) {
                tmp = StringUtils.colourString(35, "HELP") + " :\n";
                for (Field f : fields) {
                    try {
                        if(cmdSplits.length > 1){
                            if(!f.get(null).toString().toUpperCase().equals(cmdSplits[1].trim().toUpperCase())){
                                continue;
                            }
                        }
                        annotation = f.getAnnotation(ConsoleCommandParameter.class);
                        tmp += "\t" + StringUtils.colourString(36, f.get(null).toString()) + " : " + " [" + annotation.policy().toString() + "]\n" + annotation.description() + "\n";
                    } catch (Exception e) {}
                }
                syslogInfo(tmp.substring(0, tmp.length() - 1));
                continue;
            }
            if (command.equals(HISTORY)) {
                tmp = "";
                int i = 0;
                for (String cmd : history){
                    tmp += "[" + i + "] : " + cmd + "\n";
                    i++;
                }
                syslogInfo(tmp);
                continue;
            }
            try {
                CommonEntity commonEntity = new CommonEntity();
                commonEntity.setCommand(Command.CONSOLE_MANAGE_COMMAND);
                commonEntity.setBody(allCommand.getBytes(StandardCharsets.UTF_8));
                client.send(commonEntity);
            } catch (Exception ex) {
                syslogInfo("error");
            }
        } while (true);
        input.close();
    }

    public static Logger getLogger(Class<?> clazz) {
        Logger logger = Logger.getLogger(clazz);
        logger.setLevel(Level.toLevel(LOGGER_LEVEL));
        return logger;
    }

    public static void syslogInfo(String info) {
        System.out.println((isOutEnter ? "\n" : "") + info);
        System.out.print(COMMAND_PREFIX);
        isOutEnter = true;
    }

    public static void syslogInfon(String info) {
        System.out.print(info);
        System.out.print(COMMAND_PREFIX);
        isOutEnter = true;
    }

    public static AsymmetricEncryption.KeyStringPair getKeyStringPair() {
        return keyStringPair;
    }

    public static void setKeyStringPair(AsymmetricEncryption.KeyStringPair keyStringPair) {
        Console.keyStringPair = keyStringPair;
    }

    public static String getServerPublicKey() {
        return serverPublicKey;
    }

    public static void setServerPublicKey(String serverPublicKey) {
        Console.serverPublicKey = serverPublicKey;
    }
}
